package com.protocols.sctp.m3ua.sccp.tcap.map;
import java.rmi.*;

import java.rmi.registry.*;

import java.rmi.server.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.TreeMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import java.awt.geom.GeneralPath;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.*;

import javax.print.attribute.standard.MediaSize.Other;

import com.Main.MyServiceInterface;
import com.Main.MyServiceThread;
import com.Main.ReceiveMessageInterface;
import com.asterisk.AmiManagerInterface;
import com.protocols.sctp.m3ua.M3uaHandler;
import com.protocols.sctp.m3ua.M3uaPayLoadData;
import com.protocols.sctp.m3ua.sccp.SccpHandler;
import com.protocols.sctp.m3ua.sccp.SccpUnitDataMsg;
import com.protocols.GeneralProtocolData;
import com.protocols.ProtoOperations;
import com.protocols.sctp.TcpHandler;
import com.protocols.sctp.SctpHandlerInterface;
import com.protocols.sctp.m3ua.sccp.SccpHandlerInterface;
import com.protocols.sctp.m3ua.sccp.tcap.TcapBegin;
import com.protocols.sctp.m3ua.sccp.tcap.TcapHandlerInterface;
import com.protocols.sctp.m3ua.sccp.tcap.camel.CamelInitialDP;
import com.protocols.sctp.m3ua.sccp.tcap.camel.CamelReleaseCall;
import com.protocols.sctp.m3ua.sccp.tcap.camel.CamelSessionInfo;


 

public class MapHandler extends java.rmi.server.UnicastRemoteObject implements MapHandlerInterface,MyServiceInterface{

	// Configuring the Logger
	
	private TcapHandlerInterface TcapHandler=null;
	private HashMap<Long,GeneralProtocolData> MapResponses = new HashMap<Long,GeneralProtocolData>();
	private HashMap<Long,MapSessionInfo> SessionInfo = new HashMap<Long,MapSessionInfo>();
	private String HomeGT="";
	
	private Registry Registry;  
	private Logger Log = Logger.getLogger(this.getClass().getName());    
	private Boolean logInfoEnabled=false;
    
    
    // Default Configuration
	private int layerServiceport=9001;
	private String tcaphandlerurl= "TcapHandler";		
	private String maphandlerurl= "MapHandler";
	private int ConnRetry=3000;		
	private String logfile= "var/log/map.log";
	private Level loglevel= Level.OFF;
	
    
	public MapHandler() throws RemoteException  {	
		super();
		
		
	}

    public static void main(String[] args) throws InterruptedException, IOException, NotBoundException {
		
		
			  
	
	}
	
	
	public void initialise()throws Exception {	
		
		Properties properties=null;
		try{			
			properties = new java.util.Properties();
			properties.load( new FileInputStream("General.conf"));
		}catch( IOException e){
			System.out.println("Couldn't locate the Config File");				
			System.exit(0);
		}			
		logfile= properties.getProperty("map.log.filepath");
		loglevel= Level.parse(properties.getProperty("map.log.level"));
		
		FileHandler filehandler=null;
		try{
			filehandler= new FileHandler(logfile,true);
			filehandler.setFormatter(new SimpleFormatter());
			Log.addHandler(filehandler);
			Log.setLevel(loglevel);				
		}catch( IOException e){
			System.out.println("Issue Opening the Log File. Setting the Log Level to 'OFF'");	
			Log.setLevel(Level.OFF);	
		}
		
		logInfoEnabled=Log.isLoggable(Level.INFO);	
		
		layerServiceport=Integer.valueOf(properties.getProperty("layerservice.port"));
		tcaphandlerurl= properties.getProperty("tcap.layerservice.url");
		maphandlerurl= properties.getProperty("map.layerservice.url");
		ConnRetry=Integer.valueOf(properties.getProperty("connectionretryinterval"));
		HomeGT=properties.getProperty("map.homegt");
		
			
		try{
			Registry = LocateRegistry.createRegistry( layerServiceport );
			if(logInfoEnabled)Log.info("Created Registry on Port:"+layerServiceport);
		}catch(RemoteException e){
			Registry = LocateRegistry.getRegistry( layerServiceport );	
			if(logInfoEnabled)Log.info("Obtained Registry from Port:"+layerServiceport);
		}
			
		
		Registry.rebind(maphandlerurl, this);			
		if(logInfoEnabled)Log.info("Bound Map Handler on Url:"+maphandlerurl);
		
			
		
	}

		
	public void establishTcapConnection() throws Exception
	{
		
		Boolean retry=true;
		while(retry)
		{
			try{
				TcapHandler=(TcapHandlerInterface)(Registry.lookup(tcaphandlerurl));
				retry=false;
			}catch(Exception e){	
				Log.severe("Couldn't Obtained the TcapHandler Instance. Retry in "+ConnRetry+" Secs."+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
				Thread.sleep(ConnRetry);
			}
		}
		if(logInfoEnabled)Log.info("Obtained TcapHandler Instance");
	}
	@Override
	public GeneralProtocolData tcapToMap(long sessionid, byte input [],byte mapDialogPdu[],int OPC, int DPC, String OGT, String DGT, long OrigTransactionID, long DestTransactionID) throws RemoteException{
		try{
			
			MapSessionInfo thissessioninfo;
			synchronized (SessionInfo) {
				thissessioninfo= SessionInfo.get(DestTransactionID);
			}		
			
			int count=0;
			while (input.length>0 && ++count<3){	 
				if( (input[0]&0xff)==0xa1)	// Map INVOKE
				{
					if(logInfoEnabled)Log.info(sessionid+":Received Map Invoke HomeGT:"+DGT+" HLRGT:"+OGT);
					
					MapInvoke map= new MapInvoke();				
					map.decode(input);
					
									
					if (map.getOpCode(true)==59){	// MapInvokeProcessUSSRequest
						if(logInfoEnabled)Log.info(sessionid+":Received Map MapInvokeProcessUSSRequest");
						MapInvokeProcessUSSRequest processUSSRequest = new MapInvokeProcessUSSRequest();
						input = processUSSRequest.decode(input);	
						
						MapOpen mapopen = new MapOpen();
						mapopen.decode(mapDialogPdu);
						
						if(logInfoEnabled)Log.info(sessionid+": Adding Session Info");
						synchronized(SessionInfo){
							SessionInfo.put(DestTransactionID, new MapSessionInfo( processUSSRequest.getMsisdn(), OrigTransactionID,80,OGT));
						}
						
						
						if(logInfoEnabled)Log.info(sessionid+":Creating and Handing over to the Chat Logic Executer");
						MyServiceThread  UssdChat = new MyServiceThread(new ChatServiceHandler(processUSSRequest,mapopen,OPC, DPC, OGT, DGT,OrigTransactionID, DestTransactionID, layerServiceport));
						UssdChat.start();
						
						
					}
					else if (map.getOpCode(true)==60){	// MapInvokeUnstructuredSSRequest
						if(logInfoEnabled)Log.info(sessionid+":Received Map MapInvokeUnstructuredSSRequest");
						MapInvokeUnstructuredSSRequest UnstructuredSSRequest = new MapInvokeUnstructuredSSRequest();
						input = UnstructuredSSRequest.decode(input);
						
						
						
					}
					else if (map.getOpCode(true)==61){	// MapInvokeUnstructuredSSNotify
						if(logInfoEnabled)Log.info(sessionid+":Received Map MapInvokeUnstructuredSSNotify");
						MapInvokeUnstructuredSSNotify unstructuredSSNotify = new MapInvokeUnstructuredSSNotify();
						input = unstructuredSSNotify.decode(input);				
						
					}
					
				}
				else if( (input[0]&0xff)==0xa2)	// Map Return Result
				{
					Log.info(sessionid+":Received Map  Return Result");
					MapReturnResultLast mapReturnResultLast = new MapReturnResultLast();
					input = mapReturnResultLast.decode(input);		
					
						if (mapReturnResultLast.getOpCode(true)==60){	// MapReturnResultUnstructuredSSRequest
							if(logInfoEnabled)Log.info(sessionid+":Received Map MapReturnResultUnstructuredSSRequest");
							MapReturnResultUnstructuredSSRequest returnResultUnstructuredSSRequest = new MapReturnResultUnstructuredSSRequest();
							input = returnResultUnstructuredSSRequest.decode(input);	
							if(logInfoEnabled)Log.info(sessionid+":Addding MapReturnResultUnstructuredSSRequest to tyhe Response Queue");
							synchronized (MapResponses) {						
								MapResponses.put(DestTransactionID, returnResultUnstructuredSSRequest);						
							}
						}
						else if (mapReturnResultLast.getOpCode(true)==0){	// No OpCode
							if(logInfoEnabled)Log.info(sessionid+":Addding mapReturnResultLast to tyhe Response Queue");
							synchronized (MapResponses) {						
								MapResponses.put(DestTransactionID, mapReturnResultLast);						
							}
						}				
					
				}
				
			}
		}catch(Exception e){
			Log.warning(sessionid+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace())+ProtoOperations.getHexString(input));

		}
		return null;
	}

	

	@Override
	public String ping() throws RemoteException{
		// TODO Auto-generated method stub
		return "pong";
	}

	@Override
	public void startLayer() throws Exception {
		initialise();
		establishTcapConnection();
		
	}

	@Override
	public Boolean sendUSSDNotify(long SourceTransactionID, long dataCodingScheme, String ussdString,long alertingPattern, String msisdn, int tcapTimer  )throws RemoteException {

		if(logInfoEnabled)Log.info(SourceTransactionID+": Received an sendUSSDNotify");
		
		MapSessionInfo thissessioninfo;
		synchronized (SessionInfo) {
			thissessioninfo= SessionInfo.get(SourceTransactionID);
		}		
		
		try{	
			
			MapInvokeUnstructuredSSNotify ussdNotify = new MapInvokeUnstructuredSSNotify();		
			ussdNotify.setDataCodingScheme(dataCodingScheme);
			ussdNotify.setUSSDString(ussdString);
			//ussdNotify.setAlertingPattern(alertingPattern);
			//ussdNotify.setMsisdn(0x91, msisdn);
			
			int nextInvokeID=thissessioninfo.getLastInvokedID()+1;
			ussdNotify.setInvokeID(nextInvokeID);
			thissessioninfo.setLastInvokedID(nextInvokeID);
			
			if(logInfoEnabled)Log.info(SourceTransactionID+":Sending USSD Notify to TCAP Layer Stream");
			TcapHandler.mapToTcap(SourceTransactionID, ussdNotify,null,HomeGT, thissessioninfo.getOtherPartyGlobalTitle(), SourceTransactionID,thissessioninfo.getOtherPartyTransactionID());					
			
			long msgsenttime=new Date().getTime();
			
			if(logInfoEnabled)Log.info(SourceTransactionID+":Waiting for the Response to the USSD Notify.");
			
			while((new Date().getTime()-msgsenttime)<tcapTimer){
				
				GeneralProtocolData  response=null;
				Thread.sleep(500);
				synchronized (MapResponses) {					
					response= MapResponses.remove(SourceTransactionID);			
					
				}			
				
				if (!(response==null)){						
					return true;
				}				
			}
			
			Log.warning(SourceTransactionID+":Didn't receive any Response for the USSD Notify winthin: "+tcapTimer+" ms. Destination GT:"+thissessioninfo.getOtherPartyGlobalTitle());
			
			
			
		}catch(RemoteException e){
			Log.severe(SourceTransactionID+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}catch(Exception e){
			Log.severe(SourceTransactionID+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}

		synchronized(SessionInfo){
			SessionInfo.remove(SourceTransactionID);
		}
		return false;
		
	}

	@Override
	public String sendUSSDRequest(long SourceTransactionID, long dataCodingScheme, String ussdString,long alertingPattern, String msisdn, int tcapTimer )throws RemoteException{
		

		if(logInfoEnabled)Log.info(SourceTransactionID+": Received an sendUSSDRequest");
		
		MapSessionInfo thissessioninfo;
		synchronized (SessionInfo) {
			thissessioninfo= SessionInfo.get(SourceTransactionID);
		}		
		
		try{	
			
			MapInvokeUnstructuredSSRequest ussdRequest = new MapInvokeUnstructuredSSRequest();		
			ussdRequest.setDataCodingScheme(dataCodingScheme);
			ussdRequest.setUSSDString(ussdString);
			ussdRequest.setAlertingPattern(alertingPattern);
			//ussdRequest.setMsisdn(0x91, msisdn);
			
			int nextInvokeID=thissessioninfo.getLastInvokedID()+1;
			ussdRequest.setInvokeID(nextInvokeID);
			thissessioninfo.setLastInvokedID(nextInvokeID);
			
			if(logInfoEnabled)Log.info(SourceTransactionID+":Sending USSD Request to TCAP Layer Stream");
			TcapHandler.mapToTcap(SourceTransactionID, ussdRequest,null,HomeGT, thissessioninfo.getOtherPartyGlobalTitle(), SourceTransactionID,thissessioninfo.getOtherPartyTransactionID());					
			
			long msgsenttime=new Date().getTime();
			
			while((new Date().getTime()-msgsenttime)<tcapTimer){
				
				GeneralProtocolData  response=null;
				if(logInfoEnabled)Log.info(SourceTransactionID+":Waiting for the Response to the USSD Request.");
				Thread.sleep(500);
				synchronized (MapResponses) {					
					response= MapResponses.remove(SourceTransactionID);							
				}			
				
				if (!(response==null)){
					
					
					
					
					
					MapReturnResultUnstructuredSSRequest mapresponse =  (MapReturnResultUnstructuredSSRequest)response;
					return mapresponse.getUSSDString();
				}				
			}
			
			Log.warning(SourceTransactionID+":Didn't receive any Response for the USSD Notify winthin: "+tcapTimer+" ms. Destination GT:"+thissessioninfo.getOtherPartyGlobalTitle());
			
			
			
		}catch(RemoteException e){
			Log.severe(SourceTransactionID+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}catch(Exception e){
			Log.severe(SourceTransactionID+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}

		synchronized(SessionInfo){
			SessionInfo.remove(SourceTransactionID);
		}
		return null;
		
	}


	

   
   

   
}